Skip to content

[pull] main from MetaMask:main#591

Merged
pull[bot] merged 11 commits into
Reality2byte:mainfrom
MetaMask:main
Mar 10, 2026
Merged

[pull] main from MetaMask:main#591
pull[bot] merged 11 commits into
Reality2byte:mainfrom
MetaMask:main

Conversation

@pull
Copy link
Copy Markdown

@pull pull Bot commented Mar 10, 2026

See Commits and Changes for more details.


Created by pull[bot] (v2.0.0-alpha.4)

Can you help keep this open source service alive? 💖 Please sponsor : )

cmd-ob and others added 11 commits March 10, 2026 18:06
- Simplified the network controller setup by removing the nested
providerConfig object in multiple test files.
- Updated tests across various modules to directly set chainId, rpcUrl,
type, nickname, and ticker properties.
- Ensured consistency in the network configuration for local RPC and
other custom networks.

<!--
Please submit this PR as a draft initially.
Do not mark it as "Ready for review" until the template has been
completely filled out, and PR status checks have passed at least once.
-->

## **Description**

<!--
Write a short description of the changes included in this pull request,
also include relevant motivation and context. Have in mind the following
questions:
1. What is the reason for the change?
2. What is the improvement/solution?
-->

  ## Summary

- **Creates `tests/framework/fixtures/types.ts`** — single source of
truth for all fixture-related TypeScript types. Imports
`NetworkState`, `AccountsControllerState`, `PreferencesState`, and
`AccountTreeControllerState` from MetaMask packages; writes
minimal hand-crafted interfaces for the remaining controllers. Exports
the composed `Fixture → FixtureState →
EngineBackgroundState` hierarchy plus `ProviderConfig`,
`UserKeyringState`, `UserSnapState`, `UserPermissionState`, and
  `DeepPartial<T>`.

- **Replaces `private fixture: any` with `private fixture!: Fixture`**
in `FixtureBuilder.ts` — TypeScript now catches invalid
  fixture modifications at compile time.

- **Changes `withNetworkController()` signature** from
`withNetworkController({ providerConfig: {...} })` to
`withNetworkController(providerConfig: ProviderConfig)` (flat, no
wrapper). Updates all 53 call sites across `tests/smoke/` and
  `tests/regression/`.

- **Types 8 previously-`any` methods:** `withPermissionController`,
`withPreferencesController`, `withAccountTreeController`,
`withSnapController`, `withUserProfileKeyRing`,
`withUserProfileSnapUnencryptedState`, `withUserProfileSnapPermissions`,
`withTokensForAllPopularNetworks`. Removes all 8 `eslint-disable
@typescript-eslint/no-explicit-any` suppressions that guarded
  them.

- **Removes `withState()`** callers should use the typed builder methods
instead.

- **Additional type-accuracy fixes** surfaced by the stricter typing:
`BrowserTab.isArchived` added to interface, `withAsyncState`
parameter tightened to `Record<string, string>`,
`RpcEndpointType.Custom` enum used in all `rpcEndpoints` type fields
(was plain
string `'custom'`), `networkConfigurationsByChainId` index assignments
cast correctly.

- **JSDoc cleanup:** removed redundant `{type}` from all `@param` and
`@returns` tags (26 + 34 occurrences) — types are declared
  in TypeScript signatures, not JSDoc.

  ## Breaking change

`withNetworkController()` call sites must be updated. Before:
`.withNetworkController({ providerConfig: { chainId, rpcUrl, type }
})`. After: `.withNetletworkController({ chainId, rpcUrl, type })`. All
existing call sites in this repo are already updated.

## **Changelog**

<!--
If this PR is not End-User-Facing and should not show up in the
CHANGELOG, you can choose to either:
1. Write `CHANGELOG entry: null`
2. Label with `no-changelog`

If this PR is End-User-Facing, please write a short User-Facing
description in the past tense like:
`CHANGELOG entry: Added a new tab for users to see their NFTs`
`CHANGELOG entry: Fixed a bug that was causing some NFTs to flicker`

(This helps the Release Engineer do their job more quickly and
accurately)
-->

CHANGELOG entry:

## **Related issues**

Fixes:

## **Manual testing steps**

```gherkin
Feature: my feature name

  Scenario: user [verb for user action]
    Given [describe expected initial app state]

    When user [verb for user action]
    Then [describe expected outcome]
```

## **Screenshots/Recordings**

<!-- If applicable, add screenshots and/or recordings to visualize the
before and after of your change. -->

### **Before**

<!-- [screenshots/recordings] -->

### **After**

<!-- [screenshots/recordings] -->

## **Pre-merge author checklist**

- [x] I've followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile
Coding
Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md).
- [x] I've completed the PR template to the best of my ability
- [x] I've included tests if applicable
- [x] I've documented my code using [JSDoc](https://jsdoc.app/) format
if applicable
- [x] I've applied the right labels on the PR (see [labeling
guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)).
Not required for external contributors.

## **Pre-merge reviewer checklist**

- [ ] I've manually tested the PR (e.g. pull and build branch, run the
app, test code being changed).
- [ ] I confirm that this PR addresses all acceptance criteria described
in the ticket it closes and includes the necessary testing evidence such
as recordings and or screenshots.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Primarily test-infrastructure refactors, but changes the
fixture/network configuration API and fixture loading types across many
tests, which could cause widespread E2E failures if any state shape
assumptions are wrong.
> 
> **Overview**
> E2E fixture construction is made **type-safe** by introducing
`tests/framework/fixtures/types.ts` and converting `FixtureBuilder`’s
internal state from `any` to a strongly typed `Fixture`, plus tightening
several builder APIs (e.g., `withPermissionController`,
`withPreferencesController`, `withAccountTreeController`, token helpers)
and removing the generic `withState()` escape hatch.
> 
> `withNetworkController()` is simplified to accept a flat
`ProviderConfig` (no nested `{ providerConfig: ... }` wrapper), and
fixture/network endpoint typing is aligned to controller enums (e.g.,
`RpcEndpointType.Custom`). The fixture loading pipeline
(`FixtureHelper`, `FixtureServer`, and `WithFixturesOptions`) is updated
to accept either a `FixtureBuilder` or a pre-built `Fixture`, and a
broad set of smoke/regression tests are updated accordingly.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
51b142a. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
<!--
Please submit this PR as a draft initially.
Do not mark it as "Ready for review" until the template has been
completely filled out, and PR status checks have passed at least once.
-->

## **Description**

Removes fixed `width` and `height` from the AI icon SVG so it scales
correctly with the icon component (e.g. at different sizes).

1. **Reason for the change:** The AI icon had `width="24"` and
`height="24"` on the root `<svg>`, which can prevent proper scaling when
the icon is used at other sizes; the icon component typically controls
dimensions.
2. **Improvement:** Removed `width` and `height` from the SVG; kept
`viewBox="0 0 24 24"` so aspect ratio is preserved and the icon scales
with its container.

## **Changelog**

<!--
If this PR is not End-User-Facing and should not show up in the
CHANGELOG, you can choose to either:
1. Write `CHANGELOG entry: null`
2. Label with `no-changelog`

If this PR is End-User-Facing, please write a short User-Facing
description in the past tense like:
`CHANGELOG entry: Added a new tab for users to see their NFTs`
`CHANGELOG entry: Fixed a bug that was causing some NFTs to flicker`

(This helps the Release Engineer do their job more quickly and
accurately)
-->

CHANGELOG entry: null

## **Related issues**

Fixes: https://consensyssoftware.atlassian.net/browse/DSYS-553

## **Manual testing steps**

```gherkin
Feature: AI icon scaling

  Scenario: AI icon scales with container
    Given the app is open

    When user navigates to a screen that displays the AI icon (e.g. Predict, or component library / Storybook)
    Then the AI icon is displayed with correct aspect ratio
    And the icon scales correctly at different sizes (e.g. small and large)
```

## **Screenshots/Recordings**

<!-- If applicable, add screenshots and/or recordings to visualize the
before and after of your change. -->

### **Before**

<!-- [screenshots/recordings] -->

### **After**


https://github.com/user-attachments/assets/ab9f9755-d420-453a-b7c5-21bfc4855454


<!-- [screenshots/recordings] -->

## **Pre-merge author checklist**

- [x] I've followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile
Coding
Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md).
- [x] I've completed the PR template to the best of my ability
- [x] I've included tests if applicable
- [x] I've documented my code using [JSDoc](https://jsdoc.app/) format
if applicable
- [x] I've applied the right labels on the PR (see [labeling
guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)).
Not required for external contributors.

## **Pre-merge reviewer checklist**

- [ ] I've manually tested the PR (e.g. pull and build branch, run the
app, test code being changed).
- [ ] I confirm that this PR addresses all acceptance criteria described
in the ticket it closes and includes the necessary testing evidence such
as recordings and or screenshots.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Single-asset SVG attribute change with minimal blast radius; risk is
limited to potential visual sizing differences for the `ai` icon.
> 
> **Overview**
> Updates the `ai` icon asset to drop hardcoded `width` and `height` on
the root `<svg>`, relying on `viewBox="0 0 24 24"` for correct scaling
at different rendered sizes.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
c068b52. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
<!--
Please submit this PR as a draft initially.
Do not mark it as "Ready for review" until the template has been
completely filled out, and PR status checks have passed at least once.
-->

## **Description**

increase touchable area of select quotes entry

<!--
Write a short description of the changes included in this pull request,
also include relevant motivation and context. Have in mind the following
questions:
1. What is the reason for the change?
2. What is the improvement/solution?
-->

## **Changelog**

<!--
If this PR is not End-User-Facing and should not show up in the
CHANGELOG, you can choose to either:
1. Write `CHANGELOG entry: null`
2. Label with `no-changelog`

If this PR is End-User-Facing, please write a short User-Facing
description in the past tense like:
`CHANGELOG entry: Added a new tab for users to see their NFTs`
`CHANGELOG entry: Fixed a bug that was causing some NFTs to flicker`

(This helps the Release Engineer do their job more quickly and
accurately)
-->

CHANGELOG entry:  increase touchable area of select quotes entry

## **Related issues**

Fixes: https://consensyssoftware.atlassian.net/browse/SWAPS-4246

## **Manual testing steps**

```gherkin
Feature: my feature name

  Scenario: user [verb for user action]
    Given [describe expected initial app state]

    When user [verb for user action]
    Then [describe expected outcome]
```

## **Screenshots/Recordings**

<!-- If applicable, add screenshots and/or recordings to visualize the
before and after of your change. -->

### **Before**

<!-- [screenshots/recordings] -->

### **After**

<!-- [screenshots/recordings] -->

## **Pre-merge author checklist**

- [x] I've followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile
Coding
Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md).
- [x] I've completed the PR template to the best of my ability
- [x] I've included tests if applicable
- [x] I've documented my code using [JSDoc](https://jsdoc.app/) format
if applicable
- [x] I've applied the right labels on the PR (see [labeling
guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)).
Not required for external contributors.

## **Pre-merge reviewer checklist**

- [ ] I've manually tested the PR (e.g. pull and build branch, run the
app, test code being changed).
- [ ] I confirm that this PR addresses all acceptance criteria described
in the ticket it closes and includes the necessary testing evidence such
as recordings and or screenshots.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Low risk UI behavior change confined to the Bridge quote details card;
main risk is unintentionally preventing users from opening the price
impact modal if thresholds/feature flags are misconfigured.
> 
> **Overview**
> Improves the Bridge `QuoteDetailsCard` interaction by making the
*entire rate value + arrow* area tappable (larger touch target) to open
the quote selector.
> 
> Adds a `priceImpactIsSafe` check (using
`selectBridgeFeatureFlags.priceImpactThreshold.warning` with a fallback
to `AppConstants`) so pressing the price impact value only navigates to
the price impact modal when impact is at/above the warning threshold;
tests and snapshots are updated and a new test asserts no navigation
below the threshold.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
7666e16. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
…sitions (#27164)

## **Description**

`PredictClaimFooter` was internally reading the selected account address
via `selectSelectedInternalAccountAddress` selector, which could be
stale or mismatched with the actual transaction address. This change:

1. Accepts `address` as a prop so the parent passes the address directly
from the transaction context, ensuring consistency.
2. Adds an `onError` callback prop that fires when the component
encounters no won positions to claim, allowing the parent to handle the
error appropriately instead of silently rendering nothing.
3. Removes the internal `selectSelectedInternalAccountAddress` selector
dependency and the `?? '0x0'` fallback.

## **Changelog**

CHANGELOG entry: null

## **Related issues**

Fixes: MetaMask/MetaMask-planning#7093

## **Manual testing steps**

```gherkin
Feature: Predict claim confirmation

  Scenario: user claims winning prediction positions
    Given user has winning prediction positions for their account

    When user opens the claim confirmation
    Then the claim footer displays the correct positions and claim button

  Scenario: user attempts to claim with no winning positions
    Given user has no winning prediction positions for the given address

    When user opens the claim confirmation
    Then the onError callback is invoked with an error message
```

## **Screenshots/Recordings**

### **Before**

N/A - logic-only change

### **After**

N/A - logic-only change

## **Pre-merge author checklist**

- [x] I've followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile
Coding
Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md).
- [x] I've completed the PR template to the best of my ability
- [x] I've included tests if applicable
- [x] I've documented my code using [JSDoc](https://jsdoc.app/) format
if applicable
- [x] I've applied the right labels on the PR (see [labeling
guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)).
Not required for external contributors.

## **Pre-merge reviewer checklist**

- [ ] I've manually tested the PR (e.g. pull and build branch, run the
app, test code being changed).
- [ ] I confirm that this PR addresses all acceptance criteria described
in the ticket it closes and includes the necessary testing evidence such
as recordings and or screenshots.


<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Touches confirmation flow for `predictClaim` by changing how the claim
footer derives the address and how errors trigger rejection, which could
affect whether a user can confirm or gets auto-rejected in edge cases.
> 
> **Overview**
> Updates the `predictClaim` confirmation footer to derive the wallet
address from the current transaction (`txParams.from`) instead of the
selected account, avoiding mismatches when the selected account is
stale.
> 
> Adds an `onError` callback to `PredictClaimFooter` and triggers it
(while rendering `null`) when there is no transaction address or no
won/claimable positions; the parent `Footer` wires this to `onReject` so
empty claims are handled explicitly. Tests are updated to cover the new
`onError` behavior and the no-positions path.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
76bc72f. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
…able modal (#27277)

## **Description**

Fixes the "Token not available with provider" modal so that pressing the
close (`X`) button navigates the user back to the token selection screen
instead of leaving them in a stuck state.

Previously, closing the modal did nothing — the bottom sheet dismissed
but no navigation occurred, leaving the user on a blank/unresponsive
input screen. Now the close button behaves the same as the "Change
token" button, sending the user back to the token list.

## **Changelog**

CHANGELOG entry: Fixed a bug where closing the "Token not available"
modal left the user in a stuck state instead of navigating back to the
token selection screen.

## **Related issues**

Refs:
[TRAM-3329](https://consensyssoftware.atlassian.net/browse/TRAM-3329)

## **Manual testing steps**

```gherkin
Feature: Token not available modal close behavior

  Scenario: Close button navigates back to token selection
    Given the user is in the Buy flow with a region set to US (e.g. Texas)
    And a provider is selected that does not support the chosen token (e.g. Ramp Network + UNI)

    When the "Not available" modal appears
    And the user presses the close (X) button
    Then the user is navigated back to the token selection screen
    And the token list is displayed correctly

  Scenario: Change token button still works
    Given the "Not available" modal is displayed

    When the user presses the "Change token" button
    Then the user is navigated back to the token selection screen

  Scenario: Change provider button still works
    Given the "Not available" modal is displayed

    When the user presses the "Change provider" button
    Then the provider selection modal is displayed
```

## **Screenshots/Recordings**

### **Before**

<!-- Video/screenshot showing the stuck state after closing the modal
-->


https://github.com/user-attachments/assets/ff13e927-65e8-424f-9124-fbcd6a25e9a9



### **After**

<!-- Video/screenshot showing navigation back to token selection after
closing -->


https://github.com/user-attachments/assets/33d6767b-2596-461f-afa1-d8d3f7c8c357


https://github.com/user-attachments/assets/c4325a29-920e-4a12-9c8d-a3840505accd



https://github.com/user-attachments/assets/64a6d24e-26f9-4612-9b89-edd78fb7c488




## **Pre-merge author checklist**

- [x] I've followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile
Coding
Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md).
- [x] I've completed the PR template to the best of my ability
- [x] I've included tests if applicable
- [ ] I've documented my code using [JSDoc](https://jsdoc.app/) format
if applicable
- [ ] I've applied the right labels on the PR (see [labeling
guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)).
Not required for external contributors.

## **Pre-merge reviewer checklist**

- [x] I've manually tested the PR (e.g. pull and build branch, run the
app, test code being changed).
- [x] I confirm that this PR addresses all acceptance criteria described
in the ticket it closes and includes the necessary testing evidence such
as recordings and or screenshots.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Small UI navigation change scoped to a single modal, with updated test
coverage and no sensitive data/auth logic involved.
> 
> **Overview**
> Fixes the `TokenNotAvailableModal` close (X) behavior so it now
triggers the same flow as *Change token*: the bottom sheet closes and
navigation returns to `Routes.RAMP.TOKEN_SELECTION` instead of simply
dismissing.
> 
> Updates the unit test to assert navigation to token selection on
close, preventing regressions of the previously “stuck” state.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
89ca0e9. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
## **Description**

Replace manual `useState`/`useEffect`/`useRef` data-fetching in
`usePredictMarket` with React Query's `useQuery`, backed by a new
`predictMarketOptions` query factory.

**Why:** The existing implementation manually manages loading, error,
and mounted-ref state which is error-prone and duplicates logic that
React Query handles out of the box (caching, deduplication,
stale-while-revalidate, automatic cleanup on unmount).

**What changed:**
- New `queries/market.ts` — query key factory and `queryOptions` for
fetching a single market
- `queries/index.ts` — registered `market` entry in the `predictQueries`
object
- `usePredictMarket.tsx` — replaced ~100 lines of manual state
management with a single `useQuery` call
- `usePredictMarket.test.tsx` — updated tests to use
`QueryClientProvider` wrapper and `@testing-library/react-native` APIs
(`waitFor` instead of `waitForNextUpdate`)

## **Changelog**

## **Related issues**

## **Manual testing steps**

```gherkin
Feature: Predict market detail loading

  Scenario: user navigates to a market detail screen
    Given user is on the Predict markets list

    When user taps on a market card
    Then market detail screen loads with market data
    And loading indicator is shown while fetching
    And error state is shown if the fetch fails
```

## **Screenshots/Recordings**

N/A — no UI changes, internal refactor only.

### **Before**

### **After**

## **Pre-merge author checklist**

- [x] I've followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile
Coding
Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md).
- [x] I've completed the PR template to the best of my ability
- [x] I've included tests if applicable
- [x] I've documented my code using [JSDoc](https://jsdoc.app/) format
if applicable
- [ ] I've applied the right labels on the PR (see [labeling
guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)).
Not required for external contributors.

## **Pre-merge reviewer checklist**

- [x] I've manually tested the PR (e.g. pull and build branch, run the
app, test code being changed).
- [x] I confirm that this PR addresses all acceptance criteria described
in the ticket it closes and includes the necessary testing evidence such
as recordings and or screenshots.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Changes the data-fetching mechanism and hook return shape for market
details, which can affect loading/error states and caching behavior
across Predict screens despite being largely a refactor with updated
test coverage.
> 
> **Overview**
> Migrates `usePredictMarket` from manual `useState`/`useEffect`
fetching to React Query’s `useQuery`, introducing a new
`predictQueries.market` query factory (`queries/market.ts`) with stable
keys and a 10s `staleTime`.
> 
> Updates Predict UI consumers (`PredictMarketSportCardWrapper`,
`PredictMarketDetails`, and `PredictMarketDetailsActions`) and related
tests to use React Query return fields (`data`, `isLoading`, `error`)
and adjust loading/skeleton conditions accordingly, including minor test
expectation tweaks (e.g., date formatting and async waits).
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
c42adc2. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
## **Description**

The "Select Network" bottom sheet in the Bridge token selector was not
scrollable on Android.

Tested on both iOS and Android.

## **Changelog**

CHANGELOG entry: Fixed the Bridge "Select Network" bottom sheet not
being scrollable when many networks are available on Android.

## **Related issues**

Fixes: https://consensyssoftware.atlassian.net/browse/SWAPS-4252
Fixes: #27290

## **Manual testing steps**

```
Feature: Bridge token selector network filter

  Scenario: user scrolls the network list in the Select Network bottom sheet
    Given the user is on the Bridge screen
    And many networks are available

    When user taps the network filter pill above the token list
    Then the Select Network bottom sheet opens

    When user attempts to scroll the network list
    Then the list scrolls smoothly without dismissing the sheet

    When user taps a network
    Then the sheet closes and the token list is filtered to that network
```

## **Screenshots/Recordings**

### **Before**

<!-- List clipped, vertical pan dismissed the sheet instead of scrolling
-->

### **After**




https://github.com/user-attachments/assets/006154c0-b0a3-482d-8c23-be4cfbb37085





<!-- Full network list scrollable -->

## **Pre-merge author checklist**

- [ ] I've followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile
Coding
Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md).
- [ ] I've completed the PR template to the best of my ability
- [ ] I've included tests if applicable
- [ ] I've documented my code using [JSDoc](https://jsdoc.app/) format
if applicable
- [ ] I've applied the right labels on the PR (see [labeling
guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)).
Not required for external contributors.

## **Pre-merge reviewer checklist**

- [ ] I've manually tested the PR (e.g. pull and build branch, run the
app, test code being changed).
- [ ] I confirm that this PR addresses all acceptance criteria described
in the ticket it closes and includes the necessary testing evidence such
as recordings and or screenshots.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Small UI gesture-handling change limited to the Bridge network
selector; no auth, security, or data-flow logic is modified.
> 
> **Overview**
> Fixes the Bridge token selector "Select Network" bottom sheet not
scrolling on Android by switching the network list container to
`ScrollView` from `react-native-gesture-handler`, which correctly
handles scroll gestures inside the bottom sheet.
> 
> Removes the unused `BridgeNetworkSelectorBase` component that
previously wrapped children in a bottom sheet + `react-native`
`ScrollView`.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
d3b24db. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
## **Description**

Fixes the "Change provider" link in the payment selection modal so it
remains clickable while payment methods are loading.

Previously, the "Change provider" text was rendered as plain,
non-interactive text during the loading state (same grey color as
surrounding text, no `onPress` handler). Now it stays styled as a
tappable link and navigates to the provider selection screen even while
payment methods are still loading. The link is still disabled when there
is a payment method error.

## **Changelog**

CHANGELOG entry: Fixed a bug where the "Change provider" link in the
payment selection modal was not clickable while payment methods were
loading.

## **Related issues**

Refs:
[TRAM-3291](https://consensyssoftware.atlassian.net/browse/TRAM-3291)

## **Manual testing steps**

```gherkin
Feature: Change provider link clickable during loading

  Scenario: Change provider link is clickable while payment methods are loading
    Given the user is in the Buy flow
    And the user opens the "Pay with" payment selection modal
    And payment methods are still loading (skeleton placeholders visible)

    When the user taps the "Change provider" link at the bottom
    Then the provider selection modal is displayed

  Scenario: Change provider link is still clickable after payment methods load
    Given the payment selection modal is displayed
    And payment methods have finished loading

    When the user taps the "Change provider" link
    Then the provider selection modal is displayed

  Scenario: Change provider link is disabled on payment method error
    Given the payment selection modal is displayed
    And payment methods failed to load with an error

    When the user views the "Change provider" text
    Then it appears as non-interactive grey text
```

## **Screenshots/Recordings**

### **Before**

<!-- Screenshot showing "Change provider" as plain grey text during
loading -->


https://github.com/user-attachments/assets/34a72fca-3899-4b94-a489-b214aa0233c9



### **After**

<!-- Screenshot showing "Change provider" as a clickable blue link
during loading -->


https://github.com/user-attachments/assets/6aca24e1-351f-45ed-8d8e-3c736d0ae00a




## **Pre-merge author checklist**

- [x] I've followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile
Coding
Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md).
- [x] I've completed the PR template to the best of my ability
- [x] I've included tests if applicable
- [ ] I've documented my code using [JSDoc](https://jsdoc.app/) format
if applicable
- [ ] I've applied the right labels on the PR (see [labeling
guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)).
Not required for external contributors.

## **Pre-merge reviewer checklist**

- [x] I've manually tested the PR (e.g. pull and build branch, run the
app, test code being changed).
- [x] I confirm that this PR addresses all acceptance criteria described
in the ticket it closes and includes the necessary testing evidence such
as recordings and or screenshots.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Small UI behavior change gated to a modal link; limited impact and
covered by an updated unit test.
> 
> **Overview**
> Fixes the `PaymentSelectionModal` footer so **"Change provider"
remains a tappable link while payment methods are loading**, and is only
disabled/styled as non-interactive when `paymentMethodsError` is
present.
> 
> Updates the modal test to expect navigation to
`RampProviderSelectionModal` even during the loading state (and ensures
route `amount` is provided in that scenario).
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
84a1450. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
## **Description**

Fixes the "Token not available with provider" modal so that pressing the
close (`X`) button navigates the user back to the token selection screen
instead of leaving them in a stuck state.

Previously, closing the modal did nothing — the bottom sheet dismissed
but no navigation occurred, leaving the user on a blank/unresponsive
input screen. Now the close button behaves the same as the "Change
token" button, sending the user back to the token list.

## **Changelog**

CHANGELOG entry: Fixed a bug where closing the "Token not available"
modal left the user in a stuck state instead of navigating back to the
token selection screen.

## **Related issues**

Refs:
[TRAM-3329](https://consensyssoftware.atlassian.net/browse/TRAM-3329)

## **Manual testing steps**

```gherkin
Feature: Token not available modal close behavior

 Scenario: Dismiss provider selection returns to token selection
  Given the "Not available" modal is displayed
  And the user pressed "Change provider"
  When the user taps the deposit screen behind the provider selection modal
  Then the token selection screen is displayed

Scenario: Dismiss token not available modal returns to token selection
  Given the "Not available" modal is displayed
  When the user taps the deposit screen behind the modal
  Then the token selection screen is displayed
```

## **Screenshots/Recordings**

### **Before**

<!-- Video/screenshot showing the stuck state after closing the modal
-->


https://github.com/user-attachments/assets/ff13e927-65e8-424f-9124-fbcd6a25e9a9



### **After**

<!-- Video/screenshot showing navigation back to token selection after
closing -->



https://github.com/user-attachments/assets/199f4980-ce25-4873-ac68-e547c8d5950c










## **Pre-merge author checklist**

- [x] I've followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile
Coding
Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md).
- [x] I've completed the PR template to the best of my ability
- [x] I've included tests if applicable
- [ ] I've documented my code using [JSDoc](https://jsdoc.app/) format
if applicable
- [ ] I've applied the right labels on the PR (see [labeling
guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)).
Not required for external contributors.

## **Pre-merge reviewer checklist**

- [x] I've manually tested the PR (e.g. pull and build branch, run the
app, test code being changed).
- [x] I confirm that this PR addresses all acceptance criteria described
in the ticket it closes and includes the necessary testing evidence such
as recordings and or screenshots.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Small, isolated navigation change on modal dismissal with added test
coverage; minimal impact outside the ramp modal flow.
> 
> **Overview**
> Fixes `ProviderSelectionModal` so dismissing the bottom sheet (e.g.,
tapping outside/close) triggers navigation back to
`Routes.RAMP.TOKEN_SELECTION` when the modal is shown in `skipQuotes`
mode (i.e., no pending post-action).
> 
> Updates tests to mock `navigate`, capture the `BottomSheet` `onClose`
callback, and assert navigation occurs only for `skipQuotes: true` and
not for the normal quotes flow.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
69669e2. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
<!--
Please submit this PR as a draft initially.
Do not mark it as "Ready for review" until the template has been
completely filled out, and PR status checks have passed at least once.
-->

## **Description**

<!--
Write a short description of the changes included in this pull request,
also include relevant motivation and context. Have in mind the following
questions:
1. What is the reason for the change?
2. What is the improvement/solution?
-->
Fix branch deeplinks not working with the X (Twitter) app by adding a
`$deeplink_path` param in branch.io and consuming it in MetaMask Mobile.

### Context

- Platform: happens only on iOS. Android behaves differently (no
Deepview in our flow).
- User taps a t.co link in X (Twitter) → redirects to
https://metamask.app.link/1WkF6GmE40b (which is a link generated by
X/Branch.io on Tweet posting) → Branch Deepview is shown → user taps
“Get the app” → opens MetaMask via Universal Link:
https://metamask-alternate.app.link/1WkF6GmE40b?__branch_flow_type=viewapp&__branch_flow_id=...&__branch_mobile_deepview_type=1.
- Issue: The app opens correctly, but in-app we show a “page not found”
(404). So the OS and Branch open the app, but we don’t know which in-app
route corresponds to link ID 1WkF6GmE40b.

### What we found

1. No resolved URI from the SDK
- The Branch iOS SDK (and react-native-branch) give us:
- The raw URL that opened the app (e.g.
https://metamask-alternate.app.link/1WkF6GmE40b?...), and
- The params from the session (e.g. from getLatestReferringParams() /
subscribe callback).
- The SDK does not build a custom deeplink URI (e.g. metamask://swap)
from link data; it only returns the params. So we have to do routing
ourselves.

2. Implemented fix:
- Added a $deeplink_path in Branch deeplinks (this must be added to
every existing and future deeplinks that we want to post on X)
- Read it from the params and build our deeplink ourselves (e.g.
metamask://${params.$deeplink_path}) and then route inside the app.

## **Changelog**

<!--
If this PR is not End-User-Facing and should not show up in the
CHANGELOG, you can choose to either:
1. Write `CHANGELOG entry: null`
2. Label with `no-changelog`

If this PR is End-User-Facing, please write a short User-Facing
description in the past tense like:
`CHANGELOG entry: Added a new tab for users to see their NFTs`
`CHANGELOG entry: Fixed a bug that was causing some NFTs to flicker`

(This helps the Release Engineer do their job more quickly and
accurately)
-->

CHANGELOG entry: Fixed a bug that was causing deeplinks opened from X
app to fail

## **Related issues**

Fixes: https://consensyssoftware.atlassian.net/browse/GE-139
Fixes: #27140

## **Manual testing steps**

```gherkin
Feature: my feature name

  Scenario: user [verb for user action]
    Given [describe expected initial app state]

    When user [verb for user action]
    Then [describe expected outcome]
```

## **Screenshots/Recordings**

<!-- If applicable, add screenshots and/or recordings to visualize the
before and after of your change. -->

### **Before**

<!-- [screenshots/recordings] -->

### **After**

<!-- [screenshots/recordings] -->

## **Pre-merge author checklist**

- [ ] I've followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile
Coding
Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md).
- [ ] I've completed the PR template to the best of my ability
- [ ] I've included tests if applicable
- [ ] I've documented my code using [JSDoc](https://jsdoc.app/) format
if applicable
- [ ] I've applied the right labels on the PR (see [labeling
guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)).
Not required for external contributors.

## **Pre-merge reviewer checklist**

- [ ] I've manually tested the PR (e.g. pull and build branch, run the
app, test code being changed).
- [ ] I confirm that this PR addresses all acceptance criteria described
in the ticket it closes and includes the necessary testing evidence such
as recordings and or screenshots.


<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Touches deeplink routing/host allowlists and Branch handling on app
start, which can affect navigation from external links, but the change
is narrowly scoped and covered by new unit tests.
> 
> **Overview**
> Fixes Branch short-link universal links (notably from X on iOS) by
introducing `rewriteBranchUri`, which converts
`metamask-alternate.app.link/<id>` URLs into
`https://link.metamask.io/<$deeplink_path>` while preserving query
params.
> 
> `DeeplinkManager.start()` now applies this rewrite for both cold-start
Branch params (`~referring_link`) and `branch.subscribe` events,
replacing the prior `getLatestReferringParams` fallback logic.
> 
> Adds `MM_UNIVERSAL_LINK_HOST_ALTERNATE`
(`metamask-alternate.app.link`) and includes it in universal-link host
validation (`handleUniversalLink`) and MetaMask-host detection
(`util/deeplinks`), with new tests covering rewrite and routing
behavior.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
de4e3dc. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
## **Description**

Adds analytics tracking for all user interactions with the
`TokenNotAvailableModal` — the bottom sheet shown in Unified Buy v2 when
the selected token is not supported by the selected provider.

Previously only the "Change Provider" button was tracked. This PR adds:
- `Ramps Screen Viewed` on modal mount (`location: 'Token Unavailable
Modal'`)
- `Ramps Change Token Button Clicked` (new event) when user taps "Change
Token"
- `Ramps Close Button Clicked` when user taps X to dismiss

This gives full funnel visibility into what users do when they hit a
token-unavailability state.

## **Changelog**

CHANGELOG entry: null

## **Related issues**

Fixes: https://consensyssoftware.atlassian.net/browse/TRAM-3311

## **Manual testing steps**

```gherkin
Feature: Token unavailable modal analytics

  Scenario: user sees the modal and taps Change Token
    Given the user is on the Amount Input screen with a provider selected
    And the selected token is not supported by that provider

    When the token unavailable modal appears
    Then a "Ramps Screen Viewed" event fires with location "Token Unavailable Modal"

    When user taps "Change Token"
    Then a "Ramps Change Token Button Clicked" event fires with current_provider set

  Scenario: user dismisses the modal
    Given the token unavailable modal is visible

    When user taps the X close button
    Then a "Ramps Close Button Clicked" event fires with location "Token Unavailable Modal"

  Scenario: user taps Change Provider (no regression)
    Given the token unavailable modal is visible

    When user taps "Change Provider"
    Then a "Ramps Change Provider Button Clicked" event fires (unchanged behaviour)
```

## **Screenshots/Recordings**

### **Before**

Only `Ramps Change Provider Button Clicked` fired from this modal.

### **After**

All four interaction surfaces tracked. Verify via Segment debugger or
analytics logs.

## **Pre-merge author checklist**

- [x] I've followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile
Coding
Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md).
- [x] I've completed the PR template to the best of my ability
- [x] I've included tests if applicable
- [x] I've documented my code using [JSDoc](https://jsdoc.app/) format
if applicable
- [x] I've applied the right labels on the PR (see [labeling
guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)).
Not required for external contributors.

## **Pre-merge reviewer checklist**

- [ ] I've manually tested the PR (e.g. pull and build branch, run the
app, test code being changed).
- [ ] I confirm that this PR addresses all acceptance criteria described
in the ticket it closes and includes the necessary testing evidence such
as recordings and or screenshots.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Analytics-only changes plus a small close-button behavior tweak (close
vs navigate) confined to a single modal and covered by updated tests.
> 
> **Overview**
> Adds MetaMetrics tracking to the Unified Buy v2
`TokenNotAvailableModal`, firing `RAMPS_SCREEN_VIEWED` on mount and
logging button interactions for **Change token** and the modal **close
(X)**.
> 
> Introduces a new analytics event constant
`RAMPS_CHANGE_TOKEN_BUTTON_CLICKED` in `MetaMetrics.events.ts`, wires
the close button to a new `handleClose` (close only, no navigation), and
updates/extends tests to mock `useAnalytics` and assert the new event
payloads.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
cb0455f. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
@pull pull Bot locked and limited conversation to collaborators Mar 10, 2026
@pull pull Bot added the ⤵️ pull label Mar 10, 2026
@pull pull Bot merged commit 06877a8 into Reality2byte:main Mar 10, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

9 participants